package it.polito.let.java;

import it.polito.let.filesystem.FileConstants;
import it.polito.let.filesystem.FileGenericError;
import it.polito.let.filesystem.FileUtil;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

public class JavaProgram {

	private String pkg = "";
	private JavaClass mainClass;
	private String name;
	private Map <String, JavaClass> classes = new HashMap<String, JavaClass>();
	private JavaMethod main = null;

	public JavaProgram (String name, boolean putMain) {
		this.name = name;
		mainClass = new JavaClass(name, putMain);
		classes.put(name, mainClass);
		if (putMain)
			main  = mainClass.getMain();
	}

	public JavaProgram(String name) {
		this(name,false);
	}

	public void setPackage(String pkg) {
		this.pkg = pkg;
	}

	public void addImport(String imp) {
		mainClass.addImport(imp);
	}

	public void addImport(String className, String imp) throws ClassNotFoundException {
		if (!classes.containsKey(className))
			throw new ClassNotFoundException(className);
		classes.get(className).addImport(imp);
	}

	public void addSuffix(String suff) {
		mainClass.addSuffix(suff);
	}

	public void addSuffix(String className, String suff) throws ClassNotFoundException {
		if (!classes.containsKey(className))
			throw new ClassNotFoundException(className);
		classes.get(className).addSuffix(suff);
	}

	public JavaMethod getMain() {
		return main;
	}

	public JavaMethod addMethod(String[] modifiers, String type, String name, String[] params) throws DuplicateMethodException {
		return mainClass.addMethod(modifiers,type, name, params);
	}

	public JavaMethod addMethod(String className, String[] modifiers, String type, String name, String[] params) throws DuplicateMethodException, ClassNotFoundException {
		if (!classes.containsKey(className))
			throw new ClassNotFoundException(className);
		return classes.get(className).addMethod(modifiers,type, name, params);
	}	

	public void addSerAttribute(String name, Serializable obj) throws DuplicateAttributeException, DuplicateMethodException {
		mainClass.addSerAttribute(name, obj);
	}

	public void addSerAttribute(String className, String name, Serializable obj) throws ClassNotFoundException, DuplicateAttributeException, DuplicateMethodException {
		if (!classes.containsKey(className))
			throw new ClassNotFoundException(className);
		classes.get(className).addSerAttribute(name, obj);
	}

	public JavaClass addClass(String className) throws DuplicateClassException {
		if (classes.containsKey(className))
			throw new DuplicateClassException(className);
		JavaClass newClass = new JavaClass(name);
		classes.put(className,newClass);
		return newClass;
	}

	public String toString() {
		StringBuffer result = new StringBuffer();
		for (JavaClass c:classes.values()) {
			result.append("// ========= "+c.getName()+".java =========\n\n");
			result.append("package "+pkg+";\n\n");
			result.append(c.toString());
		}
		return result.toString();
	}

	public List<File> generateJavaCode(String path) throws IOException, FileGenericError {
		List<File> fc = new Vector<File>();
		for (JavaClass c:classes.values()) {
			File newf;
			newf = new File(path,FileUtil.changeFileExtension(c.getName(),FileConstants.JAVASRC));
			BufferedWriter out = new BufferedWriter(new FileWriter(newf));
			if (pkg != null && pkg.trim().length()>0)
				out.write("package "+pkg+";\n\n");
			out.write(c.toString());
			out.close();
			fc.add(newf);
		}		
		return fc;
	}

	public void addAttribute(String type, String name, String value) throws DuplicateAttributeException {
		mainClass.addAttribute(type,name,value);		
	}

	public JavaMethod addConstructor(String[] modifiers, String[] params) throws DuplicateMethodException {
		return addMethod(modifiers,mainClass.getName(),null,params);
	}

	public JavaMethod addConstructor(String className, String[] modifiers, String[] params) throws DuplicateMethodException, ClassNotFoundException {
		return addMethod(className,modifiers, className,null,params);
	}

	public void appendStatement(String globalStatement) {
		mainClass.appendStatement(globalStatement);		
	}
}
